package com.ali.ttt.webSocket.config;

import com.ali.ttt.core.utils.ThreadUtil;

import com.ali.ttt.webSocket.handler.WebSocketFrameHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

/**
 * @Author : yuxl
 * @create 2024/7/1 10:59 AM
 */
@Slf4j
@Component
public class WebSocketServer {

    private static final int MIXED_MAX = Runtime.getRuntime().availableProcessors();
    private static final String MIXED_THREAD_AMOUNT = "mixed.thread.amount";

    private static final int max = (null != System.getProperty(MIXED_THREAD_AMOUNT)) ?
            Integer.parseInt(System.getProperty(MIXED_THREAD_AMOUNT)) : MIXED_MAX;

    // 创建连接监听reactor 轮询组
    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    // 创建连接处理reactor 轮询组
    private final EventLoopGroup workerGroup = new NioEventLoopGroup(max,ThreadUtil.getCustomThreadFactory());

    @Resource
    private WebSocketFrameHandler webSocketFrameHandler;

    @Resource
    private WebSocketServerProperties webSocketServerProperties;

    @Async("async-thread-pool")
    public void start() throws UnknownHostException {
        try {
            //服务端启动引导实例
            ServerBootstrap b = new ServerBootstrap();
            //1 设置reactor 线程
            b.group(bossGroup, workerGroup)
                    //2 设置nio类型的channel
                    .channel(NioServerSocketChannel.class)
                    //3 设置日志级别
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .localAddress(new InetSocketAddress(webSocketServerProperties.getProd()))
            ;
            //5 装配流水线
            b.childHandler(new ChannelInitializer<SocketChannel>() {
                //有连接到达时会创建一个channel
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    //HTTP 请求解码器
                    ch.pipeline().addLast(new HttpRequestDecoder());
                    //HTTP 响应编码器
                    ch.pipeline().addLast(new HttpResponseEncoder());
                    // HttpObjectAggregator 将 HTTP 消息的多个部分合成一条完整的 HTTP 消息
                    ch.pipeline().addLast(new HttpObjectAggregator(65535));
                    //WebSocket 协议处理器，配置 WebSocket 的监听 URI、协议包长度限制
                    ch.pipeline().addLast(
                            new WebSocketServerProtocolHandler(webSocketServerProperties.getPath(), webSocketServerProperties.getSubprotocols(),
                                    webSocketServerProperties.isAllowExtensions(), webSocketServerProperties.getMaxFrameSize()));
                    ch.pipeline().addLast(webSocketFrameHandler);
                }
            });
            // 6 开始绑定server
            // 通过调用sync同步方法阻塞直到绑定成功
            ChannelFuture ch = b.bind().sync();
            log.info("\n----------------------------------------------------------\n\t" +
                            "websocket服务端 启动成功\n\t" +
                            "Local: \t\t\033[1;34mws:localhost:{}{}\033[0m\n\t" +
                            "External: \t\033[1;34mws:{}:{}{}\033[0m\n\t" +
                            "----------------------------------------------------------",
                    webSocketServerProperties.getProd(),
                    webSocketServerProperties.getPath(),
                    InetAddress.getLocalHost().getHostAddress(),
                    webSocketServerProperties.getProd(),
                    webSocketServerProperties.getPath()
            );
            // 7 监听通道关闭事件
            // 应用程序会一直等待，直到channel关闭
            ChannelFuture closeFuture = ch.channel().closeFuture();
            closeFuture.sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            log.info("WebSocket服务关闭");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
